package smallfrench.moteur.interp;

import smallfrench.moteur.biblio.AClasse;
import smallfrench.moteur.biblio.AObjet;
import smallfrench.moteur.biblio.RBooleen;
import smallfrench.moteur.biblio.RChaine;
import smallfrench.moteur.biblio.RClasse;
import smallfrench.moteur.biblio.err.ErreurClasse;
import smallfrench.moteur.biblio.err.ErreurOperateurBinaireIndefini;
import smallfrench.moteur.biblio.err.ErreurOperateurIndefini;
import smallfrench.moteur.interp.err.RException;

/**
 * Classe abstraite représentant un objet smallFrench.<br>
 *
 * @author antoine1023
 */
public abstract class RObjet {

	public final RClasse classe;

	/**
	 * Crée un nouvel {@link RObjet}
	 * @param classe La classe de l’objet.
	 */
	public RObjet(RClasse classe) {
		this.classe = classe;
	}

	/**
	 * Crée un nouvel {@link RObjet}.
	 * Ne doit être appelé que par le constructeur de {@link AObjet}.
	 */
	protected RObjet() {
		classe = AClasse.getClasseSF(getClass());
	}

	/**
	 * Construit l’objet de {@link RClasse} elle-même. Ce constructeur ne doit
	 * être appelé que par {@link RClasse}.
	 * @param ignore Ignoré, ne sert qu’à différencier ce consructeur
	 */
	protected RObjet(boolean ignore) {
		classe = (RClasse) this;
	}

	/**
	 * Pour comparer plus rapidement deux objets
	 */
	@Override
	public abstract boolean equals(Object objet);

	//-------------------------------------------

	private RException opIndefini(String operateur) {

		return new ErreurOperateurIndefini(
				this, operateur).getRException();
	}

	private RException opBinIndefini(String operateur, RObjet droit) {

		return new ErreurOperateurBinaireIndefini(this, operateur, droit)
		.getRException();
	}


	/**
	 * Opérateur ternaire [], assignation d’un élément à un index donné.<br>
	 *
	 * Cette méthode doit être appelée uniquement si <code>this</code> est
	 * une classe intégrée.
	 * Sinon, on ne pourra pas surcharger l’opérateur.
	 *
	 * @param index
	 * @param valeur
	 * @return Le résultat de l’opération ou <code>null</code>, si l’opération
	 * n’est pas définie.
	 */
	protected RObjet operateurAssignationIndexBase(RObjet index, RObjet valeur) {
		return null;
	}

	/**
	 * Opérateur ternaire [], assignation d’un élément à un index donné.<br>
	 *
	 * @param index
	 * @param valeur
	 * @return Le résultat de l’opération
	 */
	public final RObjet operateurAssignationIndex(RObjet index, RObjet valeur) {
		if (classe.estIntegree) {
			RObjet r = operateurAssignationIndexBase(index, valeur);
			if (r != null)
				return r;
			throw opBinIndefini("[]=", index);
		}

		RObjet impl = classe.getMembre("operateurAssignationIndex");
		if (impl == null)
			throw opBinIndefini("[]=", index);

		return impl.appeler(new RObjet[] {index, valeur});
	}

	//--------------------------------------------------------------- Binaires

	//////////Début du code généré par genererOperateurs.py //////////

	/**
	 * Opérateur binaire []<br>
	 *
	 * Cette méthode doit être appelée uniquement si <code>this</code> est
	 * une classe intégrée.
	 * Sinon, on ne pourra pas surcharger l’opérateur.
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération ou <code>null</code>, si l’opération
	 * n’est pas définie.
	 */
	protected RObjet operateurIndexBase(RObjet autre) {
		return null;
	}

	/**
	 * Opérateur binaire []<br>
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération
	 */
	public final RObjet operateurIndex(RObjet autre) {
		if (classe.estIntegree) {
			RObjet r = operateurIndexBase(autre);
			if (r != null)
				return r;
			throw opBinIndefini("[]", autre);
		}

		RObjet impl = classe.getMembre("operateurIndex");
		if (impl == null)
			throw opBinIndefini("[]", autre);
		return impl.appeler(new RObjet[] {autre});
	}



	/**
	 * Opérateur binaire +<br>
	 *
	 * Cette méthode doit être appelée uniquement si <code>this</code> est
	 * une classe intégrée.
	 * Sinon, on ne pourra pas surcharger l’opérateur.
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération ou <code>null</code>, si l’opération
	 * n’est pas définie.
	 */
	protected RObjet operateurPlusBase(RObjet autre) {
		return null;
	}

	/**
	 * Opérateur binaire +<br>
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération
	 */
	public final RObjet operateurPlus(RObjet autre) {
		if (classe.estIntegree) {
			RObjet r = operateurPlusBase(autre);
			if (r != null)
				return r;
			throw opBinIndefini("+", autre);
		}

		RObjet impl = classe.getMembre("operateurPlus");
		if (impl == null)
			throw opBinIndefini("+", autre);
		return impl.appeler(new RObjet[] {autre});
	}



	/**
	 * Opérateur binaire -<br>
	 *
	 * Cette méthode doit être appelée uniquement si <code>this</code> est
	 * une classe intégrée.
	 * Sinon, on ne pourra pas surcharger l’opérateur.
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération ou <code>null</code>, si l’opération
	 * n’est pas définie.
	 */
	protected RObjet operateurMoinsBase(RObjet autre) {
		return null;
	}

	/**
	 * Opérateur binaire -<br>
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération
	 */
	public final RObjet operateurMoins(RObjet autre) {
		if (classe.estIntegree) {
			RObjet r = operateurMoinsBase(autre);
			if (r != null)
				return r;
			throw opBinIndefini("-", autre);
		}

		RObjet impl = classe.getMembre("operateurMoins");
		if (impl == null)
			throw opBinIndefini("-", autre);
		return impl.appeler(new RObjet[] {autre});
	}



	/**
	 * Opérateur binaire *<br>
	 *
	 * Cette méthode doit être appelée uniquement si <code>this</code> est
	 * une classe intégrée.
	 * Sinon, on ne pourra pas surcharger l’opérateur.
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération ou <code>null</code>, si l’opération
	 * n’est pas définie.
	 */
	protected RObjet operateurEtoileBase(RObjet autre) {
		return null;
	}

	/**
	 * Opérateur binaire *<br>
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération
	 */
	public final RObjet operateurEtoile(RObjet autre) {
		if (classe.estIntegree) {
			RObjet r = operateurEtoileBase(autre);
			if (r != null)
				return r;
			throw opBinIndefini("*", autre);
		}

		RObjet impl = classe.getMembre("operateurEtoile");
		if (impl == null)
			throw opBinIndefini("*", autre);
		return impl.appeler(new RObjet[] {autre});
	}



	/**
	 * Opérateur binaire /<br>
	 *
	 * Cette méthode doit être appelée uniquement si <code>this</code> est
	 * une classe intégrée.
	 * Sinon, on ne pourra pas surcharger l’opérateur.
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération ou <code>null</code>, si l’opération
	 * n’est pas définie.
	 */
	protected RObjet operateurSlashBase(RObjet autre) {
		return null;
	}

	/**
	 * Opérateur binaire /<br>
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération
	 */
	public final RObjet operateurSlash(RObjet autre) {
		if (classe.estIntegree) {
			RObjet r = operateurSlashBase(autre);
			if (r != null)
				return r;
			throw opBinIndefini("/", autre);
		}

		RObjet impl = classe.getMembre("operateurSlash");
		if (impl == null)
			throw opBinIndefini("/", autre);
		return impl.appeler(new RObjet[] {autre});
	}



	/**
	 * Opérateur binaire modulo<br>
	 *
	 * Cette méthode doit être appelée uniquement si <code>this</code> est
	 * une classe intégrée.
	 * Sinon, on ne pourra pas surcharger l’opérateur.
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération ou <code>null</code>, si l’opération
	 * n’est pas définie.
	 */
	protected RObjet operateurModuloBase(RObjet autre) {
		return null;
	}

	/**
	 * Opérateur binaire modulo<br>
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération
	 */
	public final RObjet operateurModulo(RObjet autre) {
		if (classe.estIntegree) {
			RObjet r = operateurModuloBase(autre);
			if (r != null)
				return r;
			throw opBinIndefini("modulo", autre);
		}

		RObjet impl = classe.getMembre("operateurModulo");
		if (impl == null)
			throw opBinIndefini("modulo", autre);
		return impl.appeler(new RObjet[] {autre});
	}



	/**
	 * Opérateur binaire egal<br>
	 *
	 * Cette méthode doit être appelée uniquement si <code>this</code> est
	 * une classe intégrée.
	 * Sinon, on ne pourra pas surcharger l’opérateur.
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération ou <code>null</code>, si l’opération
	 * n’est pas définie.
	 */
	protected RObjet operateurEgalBase(RObjet autre) {
		return null;
	}

	/**
	 * Opérateur binaire egal<br>
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération
	 */
	public final RObjet operateurEgal(RObjet autre) {
		if (classe.estIntegree) {
			RObjet r = operateurEgalBase(autre);
			if (r != null)
				return r;
			throw opBinIndefini("egal", autre);
		}

		RObjet impl = classe.getMembre("operateurEgal");
		if (impl == null)
			throw opBinIndefini("egal", autre);
		return impl.appeler(new RObjet[] {autre});
	}



	/**
	 * Opérateur binaire inegal<br>
	 *
	 * Cette méthode doit être appelée uniquement si <code>this</code> est
	 * une classe intégrée.
	 * Sinon, on ne pourra pas surcharger l’opérateur.
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération ou <code>null</code>, si l’opération
	 * n’est pas définie.
	 */
	protected RObjet operateurInegalBase(RObjet autre) {
		return null;
	}

	/**
	 * Opérateur binaire inegal<br>
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération
	 */
	public final RObjet operateurInegal(RObjet autre) {
		if (classe.estIntegree) {
			RObjet r = operateurInegalBase(autre);
			if (r != null)
				return r;
			throw opBinIndefini("inegal", autre);
		}

		RObjet impl = classe.getMembre("operateurInegal");
		if (impl == null)
			throw opBinIndefini("inegal", autre);
		return impl.appeler(new RObjet[] {autre});
	}



	/**
	 * Opérateur binaire inf<br>
	 *
	 * Cette méthode doit être appelée uniquement si <code>this</code> est
	 * une classe intégrée.
	 * Sinon, on ne pourra pas surcharger l’opérateur.
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération ou <code>null</code>, si l’opération
	 * n’est pas définie.
	 */
	protected RObjet operateurInfBase(RObjet autre) {
		return null;
	}

	/**
	 * Opérateur binaire inf<br>
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération
	 */
	public final RObjet operateurInf(RObjet autre) {
		if (classe.estIntegree) {
			RObjet r = operateurInfBase(autre);
			if (r != null)
				return r;
			throw opBinIndefini("inf", autre);
		}

		RObjet impl = classe.getMembre("operateurInf");
		if (impl == null)
			throw opBinIndefini("inf", autre);
		return impl.appeler(new RObjet[] {autre});
	}



	/**
	 * Opérateur binaire sup<br>
	 *
	 * Cette méthode doit être appelée uniquement si <code>this</code> est
	 * une classe intégrée.
	 * Sinon, on ne pourra pas surcharger l’opérateur.
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération ou <code>null</code>, si l’opération
	 * n’est pas définie.
	 */
	protected RObjet operateurSupBase(RObjet autre) {
		return null;
	}

	/**
	 * Opérateur binaire sup<br>
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération
	 */
	public final RObjet operateurSup(RObjet autre) {
		if (classe.estIntegree) {
			RObjet r = operateurSupBase(autre);
			if (r != null)
				return r;
			throw opBinIndefini("sup", autre);
		}

		RObjet impl = classe.getMembre("operateurSup");
		if (impl == null)
			throw opBinIndefini("sup", autre);
		return impl.appeler(new RObjet[] {autre});
	}



	/**
	 * Opérateur binaire infegal<br>
	 *
	 * Cette méthode doit être appelée uniquement si <code>this</code> est
	 * une classe intégrée.
	 * Sinon, on ne pourra pas surcharger l’opérateur.
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération ou <code>null</code>, si l’opération
	 * n’est pas définie.
	 */
	protected RObjet operateurInfegalBase(RObjet autre) {
		return null;
	}

	/**
	 * Opérateur binaire infegal<br>
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération
	 */
	public final RObjet operateurInfegal(RObjet autre) {
		if (classe.estIntegree) {
			RObjet r = operateurInfegalBase(autre);
			if (r != null)
				return r;
			throw opBinIndefini("infegal", autre);
		}

		RObjet impl = classe.getMembre("operateurInfegal");
		if (impl == null)
			throw opBinIndefini("infegal", autre);
		return impl.appeler(new RObjet[] {autre});
	}



	/**
	 * Opérateur binaire supegal<br>
	 *
	 * Cette méthode doit être appelée uniquement si <code>this</code> est
	 * une classe intégrée.
	 * Sinon, on ne pourra pas surcharger l’opérateur.
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération ou <code>null</code>, si l’opération
	 * n’est pas définie.
	 */
	protected RObjet operateurSupegalBase(RObjet autre) {
		return null;
	}

	/**
	 * Opérateur binaire supegal<br>
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération
	 */
	public final RObjet operateurSupegal(RObjet autre) {
		if (classe.estIntegree) {
			RObjet r = operateurSupegalBase(autre);
			if (r != null)
				return r;
			throw opBinIndefini("supegal", autre);
		}

		RObjet impl = classe.getMembre("operateurSupegal");
		if (impl == null)
			throw opBinIndefini("supegal", autre);
		return impl.appeler(new RObjet[] {autre});
	}



	/**
	 * Opérateur binaire et<br>
	 *
	 * Cette méthode doit être appelée uniquement si <code>this</code> est
	 * une classe intégrée.
	 * Sinon, on ne pourra pas surcharger l’opérateur.
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération ou <code>null</code>, si l’opération
	 * n’est pas définie.
	 */
	protected RObjet operateurEtBase(RObjet autre) {
		return null;
	}

	/**
	 * Opérateur binaire et<br>
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération
	 */
	public final RObjet operateurEt(RObjet autre) {
		if (classe.estIntegree) {
			RObjet r = operateurEtBase(autre);
			if (r != null)
				return r;
			throw opBinIndefini("et", autre);
		}

		RObjet impl = classe.getMembre("operateurEt");
		if (impl == null)
			throw opBinIndefini("et", autre);
		return impl.appeler(new RObjet[] {autre});
	}



	/**
	 * Opérateur binaire ou<br>
	 *
	 * Cette méthode doit être appelée uniquement si <code>this</code> est
	 * une classe intégrée.
	 * Sinon, on ne pourra pas surcharger l’opérateur.
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération ou <code>null</code>, si l’opération
	 * n’est pas définie.
	 */
	protected RObjet operateurOuBase(RObjet autre) {
		return null;
	}

	/**
	 * Opérateur binaire ou<br>
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération
	 */
	public final RObjet operateurOu(RObjet autre) {
		if (classe.estIntegree) {
			RObjet r = operateurOuBase(autre);
			if (r != null)
				return r;
			throw opBinIndefini("ou", autre);
		}

		RObjet impl = classe.getMembre("operateurOu");
		if (impl == null)
			throw opBinIndefini("ou", autre);
		return impl.appeler(new RObjet[] {autre});
	}



	/**
	 * Opérateur binaire ouex<br>
	 *
	 * Cette méthode doit être appelée uniquement si <code>this</code> est
	 * une classe intégrée.
	 * Sinon, on ne pourra pas surcharger l’opérateur.
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération ou <code>null</code>, si l’opération
	 * n’est pas définie.
	 */
	protected RObjet operateurOuexBase(RObjet autre) {
		return null;
	}

	/**
	 * Opérateur binaire ouex<br>
	 *
	 * @param autre L’opérande de droite.
	 * @return Le résultat de l’opération
	 */
	public final RObjet operateurOuex(RObjet autre) {
		if (classe.estIntegree) {
			RObjet r = operateurOuexBase(autre);
			if (r != null)
				return r;
			throw opBinIndefini("ouex", autre);
		}

		RObjet impl = classe.getMembre("operateurOuex");
		if (impl == null)
			throw opBinIndefini("ouex", autre);
		return impl.appeler(new RObjet[] {autre});
	}


	//////////Fin du code généré par genererOperateurs.py //////////

	//---------------------------------------------------------------- Unaires

	/**
	 * Opérateur unaire -<br>
	 *
	 * Cette méthode doit être appelée uniquement si <code>autre</code> et si
	 * <code>this</code> sont des types intégrés.
	 * Sinon, on ne pourra pas surcharger l’opérateur.
	 *
	 * @return Le résultat de l’opération ou <code>null</code>, si l’opération
	 * n’est pas définie.
	 */
	protected RObjet operateurMoinsUnaireBase() {
		return null;
	}

	/**
	 * Opérateur moins unaire "-".
	 * @return Résultat
	 * @throws RException
	 */
	public RObjet operateurMoinsUnaire()
			throws RException {

		throw opIndefini("-");
	}

	/**
	 * Opérateur booléen non unaire "non".
	 * @return Résultat
	 * @throws RException
	 */
	public RBooleen operateurNonUnaire() throws RException {
		throw opIndefini("non");
	}

	/**
	 * Appelle cet objet, si il est appelable.
	 * 
	 * @param parametres
	 * @return La valeur de retour
	 */
	private final RObjet appeler(RObjet[] parametres)  {
		return appeler(this, parametres);
	}

	/**
	 * Appelle cet objet, si il est appelable.
	 * 
	 * @param moi Comme {@code this}
	 * @param parametres Les paramètres de l’appel.
	 * @return RObject La valeur de retour de l’appel.
	 */
	public RObjet appeler(RObjet moi, RObjet[] parametres) {
		return new ErreurClasse(this, "Fonction");
	}

	//------------------------------------------------------------- Propriétés

	/**
	 * 
	 * @param nom
	 * @return Le membre de l’objet correspondant ou <code>null</code>, si
	 * l’objet ne possède pas ce membre.
	 */
	public RObjet getMembre(String nom) {
		return null;
	}

	//---------------------------------------------------------------- Chaines

	/**
	 * @return La valeur de l’objet en chaine.
	 */
	@Override
	public final String toString() {
		//		try {
		return enChaine().valeur;

		//		} catch (RException e) {
		//
		//			System.err.println("Erreur dans une fonction enChaine() :\n" +
		//					e.toString());
		//
		//			return "";
		//		}
	}

	/**
	 * 
	 * @return Une chaine représentant la classe.
	 */
	public abstract RChaine enChaine() ;

}
